#include "IceLink.h"
#include "IceLinkPool.h"

using namespace cm_base;

namespace cm_nat
{

    IceLink::IceLink(IceLinkPool* p_linkPool, ByteBufferPool* p_pool, SocketReactor* p_reactor,BaseTimer* p_timer)
        :m_listener(NULL),
        m_port(NULL),
        m_reactor(p_reactor),
        m_agent(NULL), 
        m_bufferPool(p_pool),
        m_timer(p_timer),
        m_linkState(STATE_NULL),
        m_linkPool(p_linkPool)
    {

    }

    IceLink::~IceLink()
    {

    }

    int IceLink::setStunAddress(SocketAddress stun)
    {
        m_stunAddress = stun;

        return CM_RES_SUCCESS;
    }

    int IceLink::setup( IPAddress hostIP )
    {
        int res = CM_RES_FAIL;

        if( !m_port ){
            m_port = new UDPPort(*m_reactor, m_bufferPool);
        }
        m_port->registerListener( this );

        res = m_port->setup( hostIP );
        if( res!=CM_RES_SUCCESS ){
            goto fail;
        }

        //get host port number
        m_hostAddress = m_port->getHostAddress();
        
        m_agent = new StunAgent(m_bufferPool, m_timer);
        
        m_agent->registerListener( this );
        
        m_linkState = STATE_START_STUN_CHECK;
        m_agent->startCheck( m_stunAddress );

        return CM_RES_SUCCESS;

    fail:
        //need to release resource here
        return CM_RES_FAIL;

    }

    int IceLink::reset()
    {
        m_listener = NULL;

        m_linkState = STATE_KEEP_STUN_CHECK;
        m_agent->startCheck( m_stunAddress );
        
        return CM_RES_SUCCESS;
    }

    void IceLink::registerListener(IceLinkListener*  p_listener)
    {
        m_listener = p_listener;
    }

    int IceLink::setPeerAddress(SocketAddress peer)
    {
        m_peerAddress = peer;
        return CM_RES_SUCCESS;
    }

    SocketAddress IceLink::getHostAddress()
    {
        return m_hostAddress;
    }

    SocketAddress IceLink::getPeerAddress()
    {
        return m_peerAddress;
    }

    SocketAddress IceLink::getStunReflexAddress()
    {
        return m_stunReflexAddress;
    }

    SocketAddress IceLink::getPeerReflexAddress()
    {
        return m_peerReflexAddress;
    }

    void IceLink::checkConnection(SocketAddress peer)
    {
        if( m_agent ){
            m_linkState = STATE_START_PEER_CHECK;
            m_agent->startCheck( peer );
            m_peerAddress = peer;

            poco_debug(g_log_nat, "IceLink::checkConnection");
            poco_debug(g_log_nat, Logger::format("peer address:$0", peer.toString()) );
        }
    }

    int IceLink::send(ByteBuffer* data)
    {
        if( m_port ){
            m_port->send( data, m_peerAddress );
            poco_debug(g_log_nat, Logger::format("send data to:$0", m_peerAddress.toString()) );
        }
        return CM_RES_SUCCESS;
    }

    void IceLink::onReceive(UDPPort* p_port, ByteBuffer* p_data)
    {
        if( StunAgent::detectMessage(p_data) )
        {
            m_agent->processMessage(p_data);
            
            SocketAddress* p_sa = (SocketAddress*)p_data->getData();
            delete p_sa;
            p_data->decreaseRef();
        }
        else
        {
            m_listener->onReceive( this, p_data );
        }
    }

    void IceLink::onToSend(StunAgent* p_agent, ByteBuffer* data,const SocketAddress& sa)
    {
        m_port->send(data, sa);
    }

    void IceLink::onCheckResult(StunAgent* p_agent, int result)
    {
        if( result == StunAgentListener::CHECK_RES_CHECK_SUCCESS){

            p_agent->keepCheck();

            if( m_linkState == STATE_START_STUN_CHECK ){

                m_linkState = STATE_KEEP_STUN_CHECK;
                m_stunReflexAddress = p_agent->getReflexAddress();
    
                m_linkPool->onCheckResult( this, CHECK_RES_STUN_CHECK_SUCCESS );
                
            }else{

                m_linkState = STATE_KEEP_PEER_CHECK;
                m_peerReflexAddress = p_agent->getReflexAddress();
                
                m_linkPool->onCheckResult(this, CHECK_RES_PEER_CHECK_SUCCESS );

            }

        }else{
            poco_debug(g_log_nat,"bind fail!");
        }
    }

    int IceLink::getLinkState()
    {
        return m_linkState;
    }

}